package domain;
import domain.exceptions.*;
import domain.Game.action;

import java.awt.Toolkit;
import java.util.Iterator;
import java.util.Vector;
import presentation.Observer;
/**
 *
 * @author Shai Givony
 */

public abstract class Agent implements Cloneable{
    
	// health conditions:
	public static final int HEALTHY		= 100;
	public static final int DEAD		= 0;
	
	
	private String     	_name;
    private action  	_lastAction;
    private String 		_assignment;
    private Square     	_square;
    private Square     	_previousSquare;
    private int        	_score;
    private Vector<Observer> _observers = new Vector<Observer>();
	private boolean 	_lastSquareWasIce;
	private int			_health;
	private boolean 	_flagLatelyTaken;
	private int			_flagsTakenByMeValue;
	private Agent		_enemy;
    
    public Agent(String name, action lastDirection, String assignment) {
        this._name          = name;
        this._lastAction = lastDirection;
        this._assignment	= assignment;
        this._square        = null;
        this._previousSquare= null;
        this._score         = Game.INITIALE_SCORE;
        this._observers     = new Vector<Observer>();
        this._lastSquareWasIce	= false;
        this._health		= Agent.HEALTHY;
        this._flagLatelyTaken = false;
        this._flagsTakenByMeValue = 0;
    }
    
    //Copy Constructor
    public Agent(Agent other){
    	this._name = new String(other.getName());
    	this._lastAction = other.getLastAction();
    	this._square = other.getSquare();		// this is not right, we know, but it's fixed later (this square is overriden by the true square)
    	this._score = other.getScore();
    	this._observers = null;
    	this._health = other._health;
    }
    
    

	public String getName() {
        return _name;
    }

    public Square getSquare() {
        return _square;
    }

    public Game.action getLastAction() {
    	return _lastAction;
    }

    public void setLastAction(action lastDirection) {
    	this._lastAction = lastDirection;
    }

    public void act(action act, Board board, Vector<Agent> agents) throws CannotMoveThereException{

    	boolean whileThinking = board != Game._board;
    	//this._flagLatelyTaken = false;
    	if (!isDead()){
    		Debug.println("act: " + act);
    		if (act == action.SHOOT){
    			shoot(agents, board);
    		}
    		Square candidateSquare;
    		if (isStandingOnIcy()){
    			act = this._lastAction;		// means: given d is ignored, and d is actually becomes last direction.
    			this._lastSquareWasIce = true;
    		}
    		else{
    			this._lastSquareWasIce = false;
    		}
    		if (act != action.SHOOT){
	    		try{
	    			candidateSquare = _square.neighbour(act, board);
	    			candidateSquare.placeAgent(this);
	    			try {
	    				_square.removeAgent();
	    			} catch (Exception e) {
	    				Debug.println(e.getMessage());
	    			}
	    			this._square = candidateSquare;
	    			this._lastAction = act;
	    			
	    			
//	    			if ((!whileThinking && this._assignment.equals("1")) ||
//	    				this._assignment.equals("2.1")){
	    			if (!whileThinking){
	    				Square myNewSquare = getSquare();
		    			if (myNewSquare.isContainFlag()){
		    				try {
		    					takeFlag(myNewSquare, board);
		    				} catch (TryingToRemoveNonExistingFlagException e) {
		    					e.printStackTrace();
		    				}
		    			}
	    			}
	    		} catch (CannotPlaceAgentHereException ex) {
	    			throw new CannotMoveThereException("Cannot move from "+ getSquare()+ " " + act + " (1)");
	    		} catch (NoSuchSquareException e){
	    			throw new CannotMoveThereException("Cannot move from "+ getSquare()+ " " + act + " (2)");
	    		} finally {
	    			if (!whileThinking){
	    				System.out.println("### " + this.toString() + " Position after move: " + _square + " ###\n");      //del
	    			}
	    		}
    		}

    		// updating score:
    		Game.increaseScoreBy(-Game.STEP_COST, this, this._enemy);

    		if (!whileThinking)
    			updateGui(0);
    	}	// end of if !isDead()
    }

	public void shoot(Vector<Agent> agents, Board board){
		Debug.println("Bang");
        Vector<Agent> agentsToShoot = scan(agents);
        for (Iterator<Agent> it = agentsToShoot.iterator(); it.hasNext();) {
            it.next().die(board);
        }
	}
	
    private Vector<Agent> scan(Vector<Agent> agents) {

        //Vector<Agent> agents = Game.getAgents();
        Vector<Agent> agentsToKill = new Vector<Agent>();
        for (Iterator<Agent> it = agents.iterator(); it.hasNext();) {
            Agent agent = it.next();
            if( agent != this && inSight(agent) ){
                agentsToKill.add(agent);
            }
        }//end of for
        return agentsToKill;
    }
    
    /**
     * 
     * @param agent to shoot
     * @return true iff there is a clean shot between this and the agent
     */
    private boolean inSight(Agent agent) {
        try {
        	//using inSight in the board, that gets as a parameter two squares and return
        	//true iff there is a they are parallel and have no obsticle between them.
            return Game._board.inSight(this.getSquare(), agent.getSquare());
        } catch (MultipleAgentsHaveTheSameLocation ex) {
            Debug.println(ex.getMessage());
        }
        return false;//TODO: CHECK IF NOT TO CHANGE
    }
    
	private boolean isStandingOnIcy() {
		return this._square instanceof SquareIce;
	}
    
    public void takeFlag(Square square, Board board) throws TryingToRemoveNonExistingFlagException{
    	boolean whileThinking = Game._board != board;

    	// updates agent's score:
        Game.increaseScoreBy(square.getFlag().getValue(), this, this._enemy);
    	this._flagsTakenByMeValue += square.getFlag().getValue();
        //setScore(getScore() + square.getFlag().getValue());
        
        
        
        // updates board:
        try{
            board.removeFlag(square);
        } catch (TryingToRemoveNonExistingFlagException ex){
            throw new TryingToRemoveNonExistingFlagException();
        }
        this._flagLatelyTaken = true;
		// updates GUI:
        if (!whileThinking)
        	updateGui(1);
    }

    public void initAgentPlaceOnBoard(Square square) {
        setSquare(square);
        try {
            Game._board.placeAgent(this, square);
        } catch (CannotPlaceAgentHereException ex) {
            Debug.println(ex.getMessage());
        }
        int cell = square.getColumn() + 1 + ( square.getRow() * Game._board.getNumberOfColumns());
        updateGui(cell);
    }
    
    //public abstract void die(Board board);
    public void die(Board board){
		boolean whileThinking = board != Game._board;
		setHealth(Agent.DEAD);
		if (!whileThinking){
			Debug.println(_name + " is dead");
			try {
				Toolkit.getDefaultToolkit().beep();
				Thread.sleep(100);
				Toolkit.getDefaultToolkit().beep();
				Thread.sleep(100);
				Toolkit.getDefaultToolkit().beep();
			} catch (InterruptedException e) {}
		}
		if (!whileThinking)
			updateGui(2);
    }
    
    public int getScore(){
        return this._score;
    }
    
    public abstract void play(Board board, Vector<Agent> agents, int maxSteps);
    
    public void setSquare(Square square) {
        this._square = square;
    }

    public void setScore(int score) {
        this._score = score;
    }
    
    public void subscribe(Observer observer){
        this._observers.addElement(observer);
    }

    protected void updateGui(int status) {
        for (Iterator<Observer> it = _observers.iterator(); it.hasNext();) {
            Observer observer = it.next();
            observer.updateGui(this, status);
        }
    }
    
    public String toString(){
        return "{Agent: "+getName() + ", Square: "+getSquare()+ ", Score: "+getScore()+"}\n";
    }
    
//    @Override
//    public Object clone(){
//    	System.out.println("Agent CLONED");
//    	try {
//			return super.clone();
//		} catch (CloneNotSupportedException e) {
//			e.printStackTrace();
//		}
//		return null;
//    }//end of clone
    
    @Override
    public boolean equals(Object other){
    	if(!(other instanceof Agent)){
    		return false;
    	}
    	else{
    		Agent otherAgent = (Agent)other;
    		return this.getName().equalsIgnoreCase(otherAgent.getName()) &&
    			   this.getSquare().equals(otherAgent.getSquare()) &&
    			   this.isDead() == otherAgent.isDead() &&
    			   this._health == otherAgent._health &&
    			   this._score == otherAgent._score &&
    			   this._lastAction == otherAgent._lastAction;
    	}
    }//end of equals

	public boolean isComingFromIce() {
		return this._lastSquareWasIce;
	}
	
	public void setHealth(int healthCondition){
		this._health = healthCondition;
	}
	
	public boolean isDead(){
		return this._health == Agent.DEAD;
	}

	public void setFlagLatelyTaken(boolean _flagLatelyTaken) {
		this._flagLatelyTaken = _flagLatelyTaken;
	}

	public boolean isFlagLatelyTaken() {
		return _flagLatelyTaken;
	}

	public int get_flagsTakenByMeValue() {
		return _flagsTakenByMeValue;
	}

	protected void setAssignment(String _assignment) {
		this._assignment = _assignment;
	}

	protected String getAssignment() {
		return _assignment;
	}

	public void setEnemy(Agent _enemy) {
		this._enemy = _enemy;
	}

	public Agent getEnemy() {
		return _enemy;
	}
	
	public void setPreviousSquare(Square previousSquare) {
		this._previousSquare = previousSquare;
	}

	public Square getPreviousSquare() {
		return this._previousSquare;
	}
}
